/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.epam.muzalevskiy.third_project.XMLParsers;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import ua.epam.muzalevskiy.third_project.knife.Blade;
import ua.epam.muzalevskiy.third_project.knife.BladeLength;
import ua.epam.muzalevskiy.third_project.knife.BladeMaterial;
import ua.epam.muzalevskiy.third_project.knife.BladeWidth;
import ua.epam.muzalevskiy.third_project.knife.Handle;
import ua.epam.muzalevskiy.third_project.knife.HandleMaterial;
import ua.epam.muzalevskiy.third_project.knife.Knife;
import ua.epam.muzalevskiy.third_project.knife.KnifeComparator;
import ua.epam.muzalevskiy.third_project.knife.KnifeHandy;
import ua.epam.muzalevskiy.third_project.knife.KnifeParametersSwitcher;
import ua.epam.muzalevskiy.third_project.knife.KnifeType;
import ua.epam.muzalevskiy.third_project.knife.Units;

/**
 * This class realized logic of
 * <code> StaxParser </code> work.
 *
 * @author Muzalevskiy Vitaliy
 */
public class StaxParser implements XMLParser {

    /**
     * <code> List </code> of <code> Knife </code> type values that 
     * parsed from XML file.
     */
    private List<Knife> knifeList = new ArrayList<Knife>();
    /**
     * Information about concrete knife.
     */
    private Knife knife;
    /**
     * Value that describe position of handler.
     */
    private ParseState state;
    /**
     * Knife ID value.
     */
    private String id;
    /**
     * Knife type value.
     */
    private KnifeType type;
    /**
     * Knife handy value.
     */
    private KnifeHandy handy;
    /**
     * Origin of knife.
     */
    private String origin;
    /**
     * Knife blade value.
     */
    private Blade blade;
    /**
     * Value of blade length.
     */
    private BladeLength bladeLength;
    /**
     * Value of blade width.
     */
    private BladeWidth bladeWidth;
    /**
     * Value of blade material.
     */
    private BladeMaterial bladeMaterial;
    /**
     * Value of blade length units.
     */
    private String bladeLengthUnits;
    /**
     * Value of blade width units.
     */
    private String bladeWidthUnits;
    /**
     * Knife handle value.
     */
    private Handle handle;
    /**
     * Value of handle material.
     */
    private HandleMaterial handleMaterial;
    /**
     * Value that describe existence of blood flow.
     */
    private boolean bloodFlow;
    /**
     * This value tall ass is this knife value.
     */
    private boolean value;

    @Override
    public void parse(String fileName) {
        try {
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            XMLStreamReader reader = inputFactory
                    .createXMLStreamReader(new FileInputStream(fileName));
            String localName;
            while (reader.hasNext()) {
                int locationType = reader.next();

                switch (locationType) {
                    case XMLStreamConstants.START_ELEMENT:
                        findElementStart(reader);
                        break;
                    case XMLStreamConstants.END_ELEMENT:
                        findElementEnd(reader);
                        break;
                }
            }
        } catch (XMLStreamException | FileNotFoundException e) {
            System.err.println(e.getMessage());
        }
    }

    @Override
    public List<Knife> getKnifeList() {
        Collections.sort(knifeList, new KnifeComparator());
        return knifeList;
    }

    /**
     * This method construct the value of <code> BladeLength </code> value.
     * @param reader value of <code> XMLStreamReader </code>.
     * @throws XMLStreamException 
     */
    private void switchState(XMLStreamReader reader) throws XMLStreamException {
        switch (state) {
            case BLADE:
                bladeMaterial = KnifeParametersSwitcher.switchBladeMaterial(
                        reader.getElementText());
                break;
            case HANDLE:
                handleMaterial = KnifeParametersSwitcher.switchHandleMaterial(
                        reader.getElementText());
                state = ParseState.BLADE;
                break;
        }
    }

    /**
     * This method describe all actions needed to do when we reach the end of
     * concrete XML tag.
     * 
     * @param reader value of <code> XMLStreamReader </code>.
     */
    private void findElementEnd(XMLStreamReader reader) {
        String localName;
        localName = reader.getLocalName();
        switch (localName) {
            case "knife":
                knife = new Knife(id, type, handy, origin,
                        blade, handle, value);
                knifeList.add(knife);
                break;
            case "blade":
                blade = new Blade(bladeLength, bladeWidth,
                        bladeMaterial);
                break;
            case "handle":
                handle = new Handle(handleMaterial, bloodFlow);
                break;
        }
    }

    /**
     * This method construct the value of <code> BladeWidth </code> value.
     * 
     * @param reader value of <code> XMLStreamReader </code>.
     * @throws XMLStreamException
     * @throws NumberFormatException 
     */
    private void getBladeWidth(XMLStreamReader reader)
            throws XMLStreamException, NumberFormatException {
        bladeWidthUnits = reader.getAttributeValue(0);
        Units widthUnits = KnifeParametersSwitcher
                .switchUnits(bladeWidthUnits);
        int width = Integer.parseInt(reader.getElementText());
        bladeWidth = new BladeWidth(widthUnits, width);
    }

    /**
     * This method construct the value of <code> BladeLength </code> value.
     * 
     * @param reader value of <code> XMLStreamReader </code>.
     * @throws XMLStreamException
     * @throws NumberFormatException 
     */
    private void getBladeLength(XMLStreamReader reader)
            throws NumberFormatException, XMLStreamException {
        bladeLengthUnits = reader.getAttributeValue(0);
        Units lengthUnits = KnifeParametersSwitcher
                .switchUnits(bladeLengthUnits);
        int length = Integer.parseInt(reader.getElementText());
        bladeLength = new BladeLength(lengthUnits, length);
    }

    /**
     * This method describe all actions needed to do when we reach the start of
     * concrete XML tag.
     * 
     * @param reader value of <code> XMLStreamReader </code>.
     * @throws XMLStreamException
     * @throws NumberFormatException 
     */
    private void findElementStart(XMLStreamReader reader)
            throws XMLStreamException, NumberFormatException {
        String localName;
        localName = reader.getLocalName();
        switch (localName) {
            case "knife":
                id = reader.getAttributeValue(0);
                break;
            case "type":
                type = KnifeParametersSwitcher
                        .swithKnifeType(reader.getElementText());
                break;
            case "handy":
                handy = KnifeParametersSwitcher
                        .switchKnifeHandy(reader.getElementText());
                break;
            case "origin":
                origin = reader.getElementText();
                break;
            case "blade":
                state = ParseState.BLADE;
                break;
            case "length":
                getBladeLength(reader);
                break;
            case "width":
                getBladeWidth(reader);
                break;
            case "material":
                switchState(reader);
                break;
            case "handle":
                state = ParseState.HANDLE;
                break;
            case "bloodFlow":
                bloodFlow = Boolean.valueOf(reader.getElementText());
                break;
            case "value":
                value = Boolean.valueOf(reader.getElementText());
                break;
        }
    }
}
